package com.own.stu.apm.agent.core.plugin.match;

import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 某个类要同时含有某几个注解匹配器
 */
public class ClassAnnotationMatch implements IndirectMatch {

    List<String> annotations = new ArrayList<String>();

    ClassAnnotationMatch(String[] annotations) {
        if (annotations == null || annotations.length == 0) {
            throw new IllegalArgumentException("annotations is nil");
        }
        this.annotations = Arrays.asList(annotations);
    }

    public ElementMatcher.Junction<? super TypeDescription> buildJunction() {
        ElementMatcher.Junction<? super TypeDescription> junction = null;
        for (String anno : annotations) {
            if (junction == null) {
                junction = getAnnotatedWith(anno);
            } else {
                 junction = junction.and(getAnnotatedWith(anno));
            }
        }
        return junction;
    }

    /**
     * @param typeDescription
     * @return true:annotations是typeDescription上注解的真子集
     */
    @Override
    public boolean isMatch(TypeDescription typeDescription) {
        /*
        annotations:["a.b.Annol","a.b.Anno2"]
        typeDescription上的注解:["a.b.Anno1","a.b.Anno3"]
         */

        List<String> annotationList = new ArrayList<>(annotations);
        AnnotationList declaredAnnotations = typeDescription.getDeclaredAnnotations();
        for (AnnotationDescription annotation : declaredAnnotations) {
            String annName = annotation.getAnnotationType().getActualName();
            annotationList.remove(annName);
        }
        return annotationList.isEmpty();
    }

    private ElementMatcher.Junction<? super TypeDescription> getAnnotatedWith(String anno) {
        return ElementMatchers.isAnnotatedWith(ElementMatchers.<TypeDescription>named(anno));
    }

    public static IndirectMatch byClassAnnotation(String... annotations) {
        return new ClassAnnotationMatch(annotations);
    }

}
